﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;

namespace LegtionMVVM
{
    /*
     * 绑定脚本处理。
     * 目前支持的语法如  Text=User.Name   (如果用代码引用，可能为  form.textBox1.Text=viewModel.User.Name)
     * 以上语法说明：
     *  Text为绑定目的，一般是View的属性或者View下的控件属性，如View.textBox1.Text；
     * 中间的一元符号总共可以用 = , < , >  这3个，=为双向绑定  <为单单把源绑定到目的端 ， >为把目的端绑定到源；
     * 最后的User.Name ， 假设有一个 ViewModel的对象，它里面有一个 User的实例对象，并且这个User有Name的属性，代码引用应该是 
     * viewModel.User.Name ，但这里因为相应的View和ViewModel是对应的，所有只写User.Name
     * 
     * 语法简化： 如果绑定目的属性为 Text并且是双向的，可以只写源路径就可以了。 如以上的语法可以简化为 User.Name 
     * 高级语法：暂时不扩展 
     * */
    internal class BindingScript
    {
        static Regex regProperty = new Regex("^[\\w]*[=<>]");
        static Regex regMode = new Regex("[=<>]");

        /// <summary>
        /// 解析脚本。
        /// </summary>
        public static ToBindControl ResolvingBindingScript<TView, TViewModel>(SourceManager<TView, TViewModel> sourceManager, 
            Control bindControl, string script )
            where TView : Form
            where TViewModel : NotifyPropertyChanged
        {
            ToBindControl toBindControl = null;

            if (string.IsNullOrEmpty(script)||bindControl==null)
                return toBindControl;

            toBindControl = new ToBindControl
            {
                Component = bindControl,
                AllScript = script
            };

            string[] lines = script.Split('\n');
            List<string> listPropertyName = Source.GetTypePropertiesName(toBindControl.Type, "");
            
            foreach (string line in lines)
            {
                string defaultLine=line.Trim(' ').Trim('　');
                if (string.IsNullOrEmpty(defaultLine))
                {
                    continue;
                }
                string sourceProperty = "";
                string propertyName = GetPropertyByLine(defaultLine, out sourceProperty);
                //跳过语法不对的
                if (string.IsNullOrEmpty(propertyName)||string.IsNullOrEmpty(sourceProperty)||!listPropertyName.Contains(propertyName))
                    continue;

                BindSource bs=new BindSource
                {
                    FullPath=propertyName,
                    DesProperty=Source.GetProperty(toBindControl.Type,propertyName)
                };
                //setMode
                Match mc=regMode.Match(line);
                if (mc.Success )
                    bs.SetBindingMode(mc.Value.ToCharArray()[0]);
                else if (sourceProperty == defaultLine)
                    bs.BindingMode = BindingMode.TwoWay;
               
                //资源 按路径处理。
                TypeSource viewModelTypeSource = Source.GetTypeSource(typeof(TViewModel));                
                ValidSourcePath(viewModelTypeSource, sourceProperty, bs);
                if (bs != null && !toBindControl.BindingSourceList.ContainsKey(propertyName))
                    toBindControl.BindingSourceList.Add(propertyName, bs);
            }
            sourceManager.bindControlList.Add(toBindControl);
            return toBindControl;
        }
        private static void ValidSourcePath(TypeSource baseTypeSource, string sourceProperty, BindSource bindSource)
        {
            string[] sourcePropertyItems = sourceProperty.Split('.');
            foreach (string pathItem in sourcePropertyItems)
            {
                Dictionary<string,string> propertyNameToTypeNames = baseTypeSource.PropertyNameToTypeFullName;
                if (propertyNameToTypeNames.ContainsKey(pathItem))
                {
                    if (sourcePropertyItems.Length == 1)
                    {
                        bindSource.PropertyKey=sourceProperty;
                        bindSource.SrcProperty = Source.GetProperty(baseTypeSource.Type, sourceProperty);
                        break;
                    }
                    TypeSource tempSource = Source.GetTypeSource(propertyNameToTypeNames[pathItem]);
                    if (tempSource == null)
                    {
                        bindSource=null;
                        break;
                    }
                    else
                    {
                        bindSource.LayerSourcePath.Add(pathItem, tempSource);
                        sourceProperty = sourceProperty.Remove(0, pathItem.Length).TrimStart('.');
                        ValidSourcePath(tempSource, sourceProperty, bindSource);
                    }
                }
                else
                {
                    bindSource = null;
                    break;
                }
            }
        }        
        public static string GetPropertyByLine(string line,out string sourceProperty)
        {
            line = line.Trim(' ').Trim('　');
            sourceProperty = line;
            Match modeMC = regMode.Match(line);
            if (modeMC.Success)
            {
                Match mc = regProperty.Match(line);
                if (mc.Success)
                {
                    sourceProperty = line.Replace(mc.Value, "");
                    return mc.Value.Replace(modeMC.Value, "");
                }
                sourceProperty = line.Replace(modeMC.Value, "");
            }            
            return "Text";
        }
    }
 
}
